package com.axend.aerosense.common.connect.tcp;

import com.axend.aerosense.common.connect.bean.DeviceConnectType;
import com.axend.aerosense.common.connect.bean.MessageEntity;
import com.axend.aerosense.common.connect.impl.IConnectCallback;
import com.axend.aerosense.common.connect.impl.IDeviceConnect;
import com.axend.aerosense.base.utils.ThreadPoolUtils;
import com.blankj.utilcode.util.LogUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.JdkLoggerFactory;

public class NettyClient implements IDeviceConnect {

    //设备服务器地址，端口
    public final static String WIFI_SERVER_IP = "10.10.100.254";
    public final static int WIFI_SERVER_PORT = 8899;
    private final static int RE_CONNECT_COUNT = 3;  //重连次数

    private NioEventLoopGroup eventLoopGroup;
    private Bootstrap bootstrap;
    private ChannelFuture parentChannelFuture;
    private Channel channel;
    private ChannelHandlerContext handlerContext;

    private IConnectCallback mCallback;
    private volatile boolean isClose = false;  //是否为关闭连接
    private NioSocketChannel nioSocketChannel;
    private int retryCount = RE_CONNECT_COUNT;

    private static class Holder{
        private static final NettyClient INSTANCE = new NettyClient();
    }

    private NettyClient(){
        init();
    }

    private void init(){
//        if (bootstrap != null){
//            return;
//        }
        eventLoopGroup = new NioEventLoopGroup(9);  //boss、worker
        InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE);
        retryCount = RE_CONNECT_COUNT;
        bootstrap = new Bootstrap()
                .group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)// 不延时，立即发送
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) {
                        ch.pipeline()
                                .addLast(new MessageCodec()) //添加自定义编码解码器
                                .addLast(new ChannelInboundHandlerAdapter(){
                                     @Override
                                     public void channelActive(ChannelHandlerContext ctx)  {
                                         channel = ctx.channel();
                                         handlerContext = ctx;
                                         nioSocketChannel = ch;
                                         LogUtils.d(ctx.channel().toString() + "  已连接");
//                                         ctx.writeAndFlush(ctx.alloc().buffer().writeBytes(ByteUtils.hexStringToBytes("AAAA55550400000200000000")));
                                         callbackState(IConnectCallback.STATE_SUCCESS);
                                     }

                                     @Override
                                     public void channelRead(ChannelHandlerContext ctx, Object msg)  {
                                         MessageEntity message = msg instanceof MessageEntity ? (MessageEntity) msg : null;
                                         LogUtils.d("读取消息:" + msg.toString());
                                         callbackReadData(message);
                                     }

                                     @Override
                                     public void channelInactive(ChannelHandlerContext ctx)  {
                                         if (isClose){
                                             LogUtils.d(ctx.channel().toString() + " 连接已断开");
                                         }else {
                                             callbackState(IConnectCallback.STATE_BROKEN);
                                             LogUtils.d(ctx.channel().toString() + " 连接中断");
                                         }
                                     }

                                     @Override
                                     public void channelUnregistered(ChannelHandlerContext ctx)  {
                                         LogUtils.d("连接失败");
                                     }
                                    @Override
                                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                        System.out.println("exceptionCaught");
                                        cause.printStackTrace();
                                        ctx.close();
                                    }
                                 });
                    }
                });
    }

    public static final NettyClient getInstance(){
        return Holder.INSTANCE;
    }

    @Override
    public void connect(){
        if (bootstrap == null || parentChannelFuture == null || isConnect()){
            init();
        }else {
            if (parentChannelFuture != null && parentChannelFuture.channel()!= null){
//                try{
//                    parentChannelFuture.channel().close().sync();//已经连接时先关闭当前连接，关闭时回调exceptionCaught进行重新连接
//                }catch (Exception e){
//                    e.printStackTrace();
//                }
                parentChannelFuture.cancel(true);
                System.out.println("channelFuture------>");
                eventLoopGroup.shutdownGracefully();

                init();
            }
        }

        isClose = false;

        ThreadPoolUtils.getInstance().execute(() -> {
            try {
                parentChannelFuture = bootstrap.connect(WIFI_SERVER_IP, WIFI_SERVER_PORT).sync();
            } catch (Exception e) {
                e.printStackTrace();
                reConnect();
            }
        });
    }

    /**
     * 重连
     */
    private void reConnect(){
        ThreadPoolUtils.getInstance().execute(()->{
            do {
                try {
                    TimeUnit.SECONDS.sleep(3);
                    if (parentChannelFuture == null){
                        parentChannelFuture = bootstrap.connect(WIFI_SERVER_IP, WIFI_SERVER_PORT).sync();
                    }else {
                        break;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }while (retryCount-- > 0);

            if (!isConnect()){
                callbackState(IConnectCallback.STATE_FAILED);
            }
        });
    }

    @Override
    public void sendMsg(Object message){
        if (channel == null
                || handlerContext == null
                || message == null ||
                !(message instanceof MessageEntity))
            return;

        ThreadPoolUtils.getInstance().execute(()->{
            channel.writeAndFlush(message);
        });
    }

    @Override
    public void close(){
        isClose = true;
        closeConnect();
    }

    @Override
    public DeviceConnectType getConnectType() {
        return DeviceConnectType.TYPE_WIFI;
    }

    private void closeConnect(){
        ThreadPoolUtils.getInstance().execute(()->{
            shutdownThread();

//            parentChannelFuture = null;
//            channel = null;
//            handlerContext = null;
//            bootstrap = null;
//            eventLoopGroup = null;
        });
    }

    private void shutdownThread(){
        if (parentChannelFuture != null && eventLoopGroup != null){
            parentChannelFuture.channel().close();
            eventLoopGroup.shutdownGracefully();
        }
    }

    @Override
    public boolean isConnect(){
        return channel != null && channel.isActive();
    }

    /**
     * Android 10及以上版本获取socket，绑定进程
     * @return
     */
    public Socket getSocket(){
        if (!isConnect()){
            return null;
        }

        NioSocketChannel nettySocket = nioSocketChannel;
        Socket javaSocket = null;
        try {
            Method  javaChannel = nettySocket.getClass().getDeclaredMethod("javaChannel", new Class[0]);
            javaChannel.setAccessible(true);
            java.nio.channels.SocketChannel javaSocketChannel = (java.nio.channels.SocketChannel)javaChannel.invoke(nettySocket);
            javaSocket = javaSocketChannel.socket();
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return javaSocket;
    }

    //设置回调
    @Override
    public void setCallback(IConnectCallback callback){
        this.mCallback = callback;
    }

    private void callbackState(int state){
        if (null != mCallback){
            mCallback.state(state);
        }else {
            LogUtils.d(this, "没有设置回调！");
        }
    }

    private void callbackReadData(MessageEntity message){
        if (null != mCallback){
            mCallback.readData(message);
        }else {
            LogUtils.d(this, "没有设置回调！");
        }
    }

}
